package de.bht.fb6.cg1.exercise3.implement;
import java.lang.reflect.Array;

import de.bht.fb6.cg1.exercise3.Ring;

/**
 * ring type from a {@link RowVectorImpl}
 *
 * @param <T> type in the rowvector
 */
class RowVectorRing<T extends Number> implements Ring<RowVectorImpl<T>> {

	/**
	 * internal values
	 */
	RowVectorImpl<T> vector;

	/**
	 * ring class
	 */
	Class<? extends Ring<T>> atomRingClass;

	/**
	 * create a new ring to a given ring
	 * @param scalarRingType ring - used as internal type
	 */
	public RowVectorRing(final Class<? extends Ring<T>> scalarRingType) {
		this.atomRingClass = scalarRingType;
		vector = zero().get();
	}

	/**
	 * create a new vector with v as value
	 * @param v internal value to set
	 */
	public RowVectorRing(final MatrixImpl<T> v) {
		set(new RowVectorImpl<T>(v));
	}

	/**
	 * provides + operation
	 * @return calculation result
	 */
	@Override
	public Ring<RowVectorImpl<T>> add(final Ring<RowVectorImpl<T>> n) {
		return new RowVectorRing<T>(vector.add(n.get()));
	}

	/**
	 * provides * operation
	 * @return calculation result
	 */
	@Override
	public Ring<RowVectorImpl<T>> times(final Ring<RowVectorImpl<T>> n) {
		if (vector.getRows() != n.get().getColumns()) {
			return n.times(this);
		}
		return new RowVectorRing<T>(vector.getTransposed().mult(n.get()));
	}

	/**
	 * provides - operation
	 * @return calculation result
	 */
	@Override
	public Ring<RowVectorImpl<T>> sub(final Ring<RowVectorImpl<T>> n) {
		return new RowVectorRing<T>(vector.sub(n.get()));
	}

	/**
	 * provides a zero element row vector
	 * @return 1x1 RowVector filled with a 0
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Ring<RowVectorImpl<T>> zero() {
		Ring<T> scalarRingElement;
		try {
			scalarRingElement = atomRingClass.newInstance();
		} catch (final Exception e) {
			throw new IllegalStateException("class is in an illegal state");
		}
		scalarRingElement = scalarRingElement.zero();
		final Ring<T>[][] start_val = (Ring<T>[][]) Array.newInstance(atomRingClass, 1, 1);
		start_val[0][0] = scalarRingElement.zero();
		return new RowVectorRing<T>(new RowVectorImpl<T>(start_val));
	}

	/**
	 * provides a 1x1 identity {@link RowVectorImpl}
	 * @return 1x1 identity row vector
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Ring<RowVectorImpl<T>> one() {
		final Ring<T>[][] tOne = (Ring<T>[][]) Array.newInstance(atomRingClass, 1, 1);
		tOne[0][0] = zero().get().createId(1, 1).getRing(0, 0);
		return new RowVectorRing<T>(new RowVectorImpl<T>(tOne));
	}

	/**
	 * get the current value
	 * @return current value
	 */
	@Override
	public RowVectorImpl<T> get() {
		return vector;
	}

	/**
	 * set the ring to a new value
	 * @param newValue new value
	 */
	@Override
	public void set(final RowVectorImpl<T> newValue) {
		vector = newValue;
		atomRingClass = newValue.getRingClass();
	}

	/**
	 * creates a numeric hashcode
	 * @return hashcode
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((atomRingClass == null) ? 0 : atomRingClass.hashCode());
		result = prime * result + ((vector == null) ? 0 : vector.hashCode());
		return result;
	}

	/**
	 * compares the object to another
	 * @param obj to compare
	 * @return true if obj equals this
	 */
	@SuppressWarnings("unchecked")
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		RowVectorRing<T> other = (RowVectorRing<T>) obj;
		if (atomRingClass == null) {
			if (other.atomRingClass != null)
				return false;
		} else if (!atomRingClass.equals(other.atomRingClass))
			return false;
		if (vector == null) {
			if (other.vector != null)
				return false;
		} else if (!vector.equals(other.vector))
			return false;
		return true;
	}
}
